/*
 * @Author: Zyunchao 18651805393@163.com
 * @Date: 2022-11-30 11:07:26
 * @LastEditors: “zhujin” zhujin@jsjngf.com
 * @LastEditTime: 2024-10-17 14:39:32
 * @FilePath: \micro-template-monorepo\_core\api\base.ts
 * @Description: 将配置表映射成一个个请求函数的核心处理器
 */
/* eslint-disable indent */
import axios from '@jsjn/micro-core-utils/http'
import { REQMethods, RequestConfig, REQParamsType } from '@jsjn/types/Request'
import { filterObj } from '@jsjn/micro-core-utils/utils'
import qs from 'qs'
import defaultPrefix from './prefix'

/**
 * 创建请求地址传入 api
 * @param {String url}
 */
const mergeUrl = (api: string, p?: string) => (!p ? `${defaultPrefix}/${api}` : `${p}/${api}`)

/**
 * 获取一个字符串中所有大括号 {} 包围的子字符串
 * @param str 
 * @returns 
 */
const extractBracedStrings = (str: string) => {
    // 匹配大括号 {} 包围的字符串，并且保留大括号内的内容
    const regex = /{([^}]+)}/g
    const matches = [];
    let match

    // 使用正则表达式的 exec 方法来循环匹配字符串中符合条件的子串
    while ((match = regex.exec(str)) !== null) {
        matches.push(match[1]) // match[1] 匹配到的是大括号内的内容
    }

    return matches
}

/**
 * 创建请求根据预设的映射表
 * @param {Array mapping}
 */
export function createRequestByMapping(mapping: Array<RequestConfig>) {
    return mapping.reduce(
        (
            modules: {
                [key: string]: (params?: any) => Promise<any>
            },
            item: RequestConfig
        ) => {
            /**
             * axios 配置对象初始化
             */
            let reqConfig: RequestConfig['axiosRequestConfig'] = item.axiosRequestConfig
                ? item.axiosRequestConfig
                : {}

            // 配置自定义请求头
            item.headers && (reqConfig['headers'] = item.headers)

            // 如果指明请求的参数类型是 keyValue，需要添加请求头 Content-Type
            if (item.paramType && item.paramType === REQParamsType.KEY_VALUE) {
                reqConfig['headers']['Content-Type'] = 'application/x-www-form-urlencoded'
            }

            // 创建请求方法（返回请求的 Promise）
            modules[item.name] = function (params?: any, replace?: Record<string, string | number>): Promise<any> {
                /**
                 * 多个代理
                 */
                let url = !item.devPrefix ? mergeUrl(item.api) : mergeUrl(item.api, item.devPrefix)

                /**
                  * 若params.zzzz =  'abc'
                 * 'xxxx/yyy/{zzzz}' 调用接口时替换url中的动态参数，变成'xxxx/yyy/abc'
                 */
                const macthes: string[] = extractBracedStrings(url)
                // const regex = /{([^}]+)}/g
                url = macthes.reduce((prev, cur) => {
                    return prev.replace(new RegExp(`{${cur}}`, 'g'), `${replace?.[cur] ?? ''}`)
                }, url)

                /**
                 * 请求可能都会有 RESTful 的参数：以 query 的形式携带 ==> 需要手动拼接到 url 上面
                 */
                let jointUrl = url
                if (item.paramType && item.paramType === REQParamsType.QUERY) {
                    if (params && Object.keys(params).length) {
                        Object.keys(params).forEach((key) => {
                            jointUrl += `/${params[key]}`
                        })
                    }
                }

                switch (item.method) {
                    case REQMethods.GET:
                    case REQMethods.DELETE:
                    case REQMethods.HEAD: {
                        /**
                         * 简单请求参数携带方式分为两种：
                         *  1. RESTful，参数以 query 的形式携带 ==> 需要手动拼接到 url 上面
                         *  2. 以 search 的形式携带，默认
                         */
                        if (!item.paramType || item.paramType !== REQParamsType.QUERY) {
                            // 过滤请求参数
                            reqConfig!['params'] = filterObj(params)
                        }

                        return axios[item.method](jointUrl, reqConfig)
                    }
                    case REQMethods.POST:
                    case REQMethods.PUT: {
                        // 如果是 KeyVlue 形式的，需要特殊处理，默认为 JSON
                        let data: any =
                            item.paramType === REQParamsType.KEY_VALUE
                                ? qs.stringify(params)
                                : params

                        // 如果是 query 形式的，清空 data，参数携带到 url 上
                        if (item.paramType === REQParamsType.QUERY) {
                            data = null
                        }

                        return axios[item.method](jointUrl, data, reqConfig)
                    }
                }
            }
            return modules
        },
        {}
    )
}
